#
#  PyMetaMonkeyAppDelegate.py
#  PyMetaMonkey
#
#  Created by Tim Cuthbertson on 22/03/08.


# cocoa bridge
from Foundation import *
from AppKit import *
from ScriptingBridge import SBApplication
from objc import ivar

# standard modules
import os
from time import sleep

# application modules
from helpers import log, threadedAction
from apps import ThirdPartyApp, AppFinder, AppIPhoto
from FileNode import FileNode

def getQLClass():
	"""
	The QLPreviewPanel class doesn't exist until runtime,
	so this returns it (once the QuickLook framework is imported)
	"""
	return NSClassFromString("QLPreviewPanel")


class PyMetaMonkeyAppDelegate(NSObject):
	"""
	The application delegate
	"""
	
	currentNodes = ivar("currentNodes")
	syncWithExternalApp = ivar("syncWithExternalApp")
	qlEnabled = ivar("qlEnabled")
	arrayController = ivar("arrayController")
	qlPanel = ivar("qlPanel")
	btnQL = ivar("buttonQL")
	wndInspector = ivar("wndInspector")
	wndBrowser = ivar("wndBrowser")
	menuQL = ivar("menuQL")
	menuBrowser = ivar("menuBrowser")
	menuInspector = ivar("menuInspector")
	qlVisible = ivar("qlVisible")
	btnLeft = ivar("btnLeft")
	btnRight = ivar("btnRight")
	btnSave = ivar("btnSave")
	btnRefresh = ivar("btnRefresh")
	btnAutoSync = ivar("btnAutoSync")
	keysEdit = ivar("keysEdit")
	commentEdit = ivar("commentEdit")
	btnEditKeys = ivar("btnEditKeys")
	btnEditComment = ivar("btnEditComment")
	btnOpenFiles = ivar("btnOpenFiles")
	btnRevealFile = ivar("btnRevealFile")
	nsApplication = ivar("nsApplication")

	# there has to be a better way than this..
	r0 = ivar("rating0")
	r1 = ivar("rating1")
	r2 = ivar("rating2")
	r3 = ivar("rating3")
	r4 = ivar("rating4")
	r5 = ivar("rating5")
	
	finderApp = None
	iPhotoApp = None
	
	def __init__(self):
		self.finderApp = AppFinder()
		self.externalApp = self.finderApp
		self.currentNodes = []
		
		# try loading quicklook:
		self.qlEnabled = NSBundle.bundleWithPath_("/System/Library/PrivateFrameworks/QuickLookUI.framework").load()
		if self.qlEnabled:
			log("Quick Look loaded!")
		else:
			log("couldn't load quicklook")
	
	def init(self):
		self.__init__()
		return self
	
	def awakeFromNib(self):
		log("awake from nib...")
		# setup some UI values
		self.syncWithExternalApp = True
		
		self.menuQL.setKeyEquivalent_("l")
		self.menuInspector.setKeyEquivalent_("i")
		self.menuBrowser.setKeyEquivalent_("b")
		
		self.btnLeft.setKeyEquivalent_("[")
		self.btnRight.setKeyEquivalent_("]")
		
		self.btnSave.setKeyEquivalent_("s")
		self.btnRefresh.setKeyEquivalent_("r")
		self.btnAutoSync.setKeyEquivalent_("S")
		self.btnOpenFiles.setKeyEquivalent_(NSString.stringWithCharacters_length_(NSDownArrowFunctionKey,1))
		self.btnRevealFile.setKeyEquivalent_("R")
		
		self.btnEditKeys.setKeyEquivalent_("k")
		self.btnEditComment.setKeyEquivalent_("K")
		
		# everything is command-key hotkeyed:
		for btn in (
			self.menuQL,
			self.menuInspector,
			self.menuBrowser,
			self.btnSave,
			self.btnRefresh,
			self.btnLeft,
			self.btnRight,
			self.btnAutoSync,
			self.btnEditKeys,
			self.btnEditComment,
			self.btnOpenFiles,
			self.btnRevealFile
		):
			btn.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		
		# and now, with the elegance of a sledgehammer...
		self.r0.setKeyEquivalent_("0")
		self.r1.setKeyEquivalent_("1")
		self.r2.setKeyEquivalent_("2")
		self.r3.setKeyEquivalent_("3")
		self.r4.setKeyEquivalent_("4")
		self.r5.setKeyEquivalent_("5")
		
		self.r0.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.r1.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.r2.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.r3.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.r4.setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.r5.setKeyEquivalentModifierMask_(NSCommandKeyMask)
	
	def applicationDidFinishLaunching_(self, sender):
		log("Application did finish launching.")
		self.refresh(True)

		t = threadedAction(self.refreshThread)
		t.run()
	
	def stopSync(self):
		"""
		Turn off syncing with external app
		"""
		self.syncWithExternalApp = False
		self.externalApp.sel = []
	
	def application_openFiles_(self, sender, files):
		log("opening files:",files)
		self.stopSync()
		self.arrayController.saveAll()
		self.currentNodes = [FileNode.alloc().initWithPath_(f) for f in files]
		self.arrayController.objectsChanged()
		self.nsApplication.replyToOpenOrPrint_(NSApplicationDelegateReplySuccess)
	
	def openFiles_(self, sender):
		sel = self.arrayController.selectedObjects()
		if len(sel) < 1:
			log("not opening any files")
		else:
			log("opening files:", sel)
			os.popen("open " + " ".join(['"' + f.path + '"' for f in sel]))
	
	def revealFile_(self, sender):
		sel = self.arrayController.selectedObjects()
		log("selection to reveal in finder:",sel)
		if len(sel) < 1:
			log("not opening any files")
			return
		self.stopSync()
		os.system(
			"osascript -e 'tell application \"Finder\"\n\
				reveal item (\"" + sel[0].path + "\" as POSIX file)\n\
				activate\n\
			end tell'")
	

	# rating callbacks for interface builder
	def rate0_(self, sender): self.rateCurrentSongs(0)
	def rate1_(self, sender): self.rateCurrentSongs(1)
	def rate2_(self, sender): self.rateCurrentSongs(2)
	def rate3_(self, sender): self.rateCurrentSongs(3)
	def rate4_(self, sender): self.rateCurrentSongs(4)
	def rate5_(self, sender): self.rateCurrentSongs(5)
	def rateCurrentSongs(self,val):
		for o in self.arrayController.selectedObjects():
			o.setRating_(val)
	
	def saveAll_(self, sender):
		self.arrayController.saveAll()
	
	def nextFile_(self,sender):
		log("next")
		if len(self.currentNodes) > 1:
			self.arrayController.selectNext_(self)
		else:
			self.thirdPartySelect(True)
			
	def prevFile_(self, sender):
		log("prev")
		if len(self.currentNodes) > 1:
			self.arrayController.selectPrevious_(self)
		else:
			self.thirdPartySelect(False)
	
	def editKeys_(self, sender):
		"""
		focus the key editing textbox
		"""
		self.wndInspector.makeKeyAndOrderFront_(self)
		self.keysEdit.becomeFirstResponder()
	
	def editComment_(self, sender):
		"""
		focus the comment editing textbox
		"""
		self.wndInspector.makeKeyAndOrderFront_(self)
		self.commentEdit.becomeFirstResponder()
	
	def thirdPartySelect(self,next):
		"""
		This function turned out to be more trouble than it's worth.
		But the code is here in case it is ever re-attempted.
		"""
		return
		# -----------------------------------------------------------------------------
		
		# sometimes left and right will be appropriate, but it's too hard to tell when.
		# up / down work right in more situations than left/right.
		if next:
			key = 126 # up
		else:
			key = 125 # down
		
		os.system(
			"osascript -e '\
				tell application \"" + self.externalApp.name + "\" to activate\n\
				tell application \"System Events\" to key code "+ str(key) + "'")


	
	def refreshThread(self):
		"""
		This action runs in its own thread, syncing selections between the finder and this application
		"""
		while True:
			# the autorelease pool is done inside each loop iteration, beause otherwise garbage
			# is never actually collected. Which kinda ruins the point...
			pool = NSAutoreleasePool.alloc().init()
			self.refresh()
			del pool
			sleep(0.5)
	
	
	def refresh_(self, sender):
		self.refresh(True)

	## set which application we want to sync selection with
	def syncModeFinder_(self,sender):
		log("finder mode!")
		self.setApp(self.finderApp)
	
	def syncModeIPhoto_(self, sender):
		log("iPhoto mode!")
		if self.iPhotoApp is None:
			self.iPhotoApp = AppIPhoto()
		self.setApp(self.iPhotoApp)
	
	def syncModeIPhotoToggle_(self, sender):
		log("toogling iPhoto mode")
		if self.iPhotoApp is None:
			self.iPhotoApp = AppIPhoto()
		
		if self.externalApp == self.iPhotoApp:
			self.setApp(self.finderApp)
		else:
			self.setApp(self.iPhotoApp)


	def setApp(self,app):
		"""
		Set the current third-party app that we're interacting with.
		"""
		if self.externalApp != app:
			app.sel = []
			self.externalApp.sel = []
			self.externalApp = app
			self.refresh(True)
	
	def qlToggle_(self,sender):
		"""
		Toggle the quickLook panel
		"""
		if self.qlEnabled:
			if self.qlPanel is None:
				self.qlPanel = getQLClass().sharedPreviewPanel()
				#self.qlPanel.setShowsAddToiPhotoButton_(False)
			
			if self.qlPanel.isOpen():
				self.qlPanel.closeWithEffect_(1)
			else:
				urls = [x.url for x in self.arrayController.arrangedObjects()]
				selected = self.arrayController.selectionIndex()
				if selected == NSNotFound:
					selected = 0
				self.qlPanel.setURLs_currentIndex_preservingDisplayState_(urls, selected, True)
				self.qlPanel.makeKeyAndOrderFrontWithEffect_(1)
				self.arrayController.qlPanel = self.qlPanel
		self.qlVisible = self.getQlVisible()
	
	def refresh(self, force = False):
		"""
		Sync selection between the browser & quicklook (this would be much better using bindings)
		Also, syncs the browser's content array with the finder's current selection
		"""
		doUpdate = False
		if self.syncWithExternalApp or force:
			if self.externalApp.selectionChanged():
				doUpdate = True
		
		if doUpdate:
			self.arrayController.saveAll()
			self.currentNodes = self.externalApp.getNodes()
			# this should be automatically hooked into a binding somewhere, but I don't know how...
			self.arrayController.objectsChanged()
			
		else:
			# if we're not updating the selection, let's make sure
			# the browser and QuickLook selections are in sync
			
			if (not self.qlPanel is None) and self.qlPanel.isOpen():
				qlCurrentIndex = self.qlPanel.indexOfCurrentURL()
				if qlCurrentIndex != self.arrayController.selectionIndex():
					self.arrayController.qlIndexChanged_(qlCurrentIndex)
					return
					
			# autosave current values every refresh
			try:
				for f in self.arrayController.selectedObjects():
					f.save()
			except Exception:
				pass
	
	# actions to toggle the windows / panels on and off
	def toggleInspector_(self,sender):
		self.toggleWindow(self.wndInspector)

	def toggleBrowser_(self,sender):
		self.toggleWindow(self.wndBrowser)
		
	def toggleWindow(self, wnd):
		if wnd.isVisible():
			wnd.orderOut_(self)
		else:
			wnd.makeKeyAndOrderFront_(self)

	# is QuickLook visible?
	def getQlVisible(self):
		log("qlVisible?")
		return (self.qlEnabled and (not self.qlPanel is None) and self.qlPanel.isOpen())



